package com.hy.dp.bagProblem.fullBag;

public class CombinationOfSum {


    /**
     * 动态规划：Carl称它为排列总和！
     * 377. 组合总和 Ⅳ
     * 力扣题目链接
     * 难度：中等
     * 给定一个由正整数组成且不存在重复数字的数组，找出和为给定目标正整数的组合的个数。
     * 示例:
     * nums = [1, 2, 3] target = 4
     *
     * 所有可能的组合为： (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)
     * 请注意，顺序不同的序列被视作不同的组合。
     * 因此输出为 7。
     *
     * 思路
     * 组合不强调顺序，(1,5)和(5,1)是同一个组合。
     *
     * 排列强调顺序，(1,5)和(5,1)是两个不同的排列。
     * 如果本题要把排列都列出来的话，只能使用回溯算法爆搜。
     *
     * 动规五部曲分析如下：
     *
     * 1. 确定dp数组以及下标的含义
     * dp[i]: 凑成目标正整数为i的排列个数为dp[i]
     *
     * 2.确定递推公式
     * dp[i]（考虑nums[j]）可以由 dp[i - nums[j]]（不考虑nums[j]） 推导出来。
     * 因为只要得到nums[j]，排列个数dp[i - nums[j]]，就是dp[i]的一部分。
     * 在动态规划：494.目标和 和 动态规划：518.零钱兑换II中我们已经讲过了，求装满背包有几种方法，递推公式一般都是dp[i] += dp[i - nums[j]];
     *
     * 3.dp数组如何初始化
     * 因为递推公式dp[i] += dp[i - nums[j]]的缘故，dp[0]要初始化为1，这样递归其他dp[i]的时候才会有数值基础。
     * 至于dp[0] = 1 有没有意义呢？
     * 其实没有意义，所以我也不去强行解释它的意义了，因为题目中也说了：给定目标值是正整数！ 所以dp[0] = 1是没有意义的，仅仅是为了推导递推公式。
     * 至于非0下标的dp[i]应该初始为多少呢？
     * 初始化为0，这样才不会影响dp[i]累加所有的dp[i - nums[j]]。
     *
     * 4.确定遍历顺序
     * 个数可以不限使用，说明这是一个完全背包。
     *
     * 得到的集合是排列，说明需要考虑元素之间的顺序。
     * 如果求组合数就是外层for循环遍历物品，内层for遍历背包。
     *
     * 如果求排列数就是外层for遍历背包，内层for循环遍历物品。
     *
     * 如果把遍历nums（物品）放在外循环，遍历target的作为内循环的话，举一个例子：计算dp[4]的时候，结果集只有 {1,3} 这样的集合，不会有{3,1}这样的集合，因为nums遍历放在外层，3只能出现在1后面！
     *
     * 所以本题遍历顺序最终遍历顺序：target（背包）放在外循环，将nums（物品）放在内循环，内循环从前到后遍历。
     *
     * 5.举例来推导dp数组
     * 我们再来用示例中的例子推导一下：
     * dp[0] = 1
     * dp[1] = dp[0] = 1
     * dp[2] = dp[0] + dp[1] = 2
     * dp[3] = dp[0] + dp[1] + dp[2] = 4
     * dp[4] = dp[1] + dp[2] + dp[3] = 7
     * @param nums
     * @param target
     * @return
     */
    public static int combinationOfSum(int [] nums,int target){
        // 1. 定义dp数组以及下标含义
        int [] dp = new int[target + 1];
        //2. 推导 递推式
        //dp[j] += dp[j - nums[i]];
        //3.初始化
        dp[0] = 1;
        //4. 循环遍历 先遍历  背包  再遍历物品  排列问题
        for (int i = 0; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                 if (i - nums[j] >= 0){
                     dp[i] += dp[i - nums[j]];
                 }
            }
        }
        //5.结果
        return dp[target];
    }


    public static void main(String[] args) {
        int [] nums = {1, 2, 3};
        int target = 4;
        System.out.println("res: "+combinationOfSum(nums,target));
    }
}
